package com.xli.sso.security.config;

import com.xli.sso.security.filter.CheckTokenFilter;
import com.xli.sso.security.filter.LoginFilter;
import com.xli.sso.security.filter.VerificationCodeFilter;
import com.xli.sso.security.handler.LoginAccessDefineHandler;
import com.xli.sso.security.handler.LoginAuthenticationHandler;
import com.xli.sso.security.handler.LoginFailureHandler;
import com.xli.sso.security.handler.LoginSuccessHandler;
import com.xli.sso.security.provider.CustomAuthorizationManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.core.session.SessionRegistry;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

    @Autowired
    AuthenticationConfiguration authenticationConfiguration;

    @Autowired
    private CheckTokenFilter checkTokenFilter;

    @Value("${login.loginUrl}")
    private String loginUrl;

    @Bean
    SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {
        //permitAll:具有所有权限，也就是匿名可以访问
        //anyRequest:其他所有的
        //authenticated:必须登录认证才能访问
        httpSecurity.authorizeHttpRequests(registry -> {
            //noNeedAuth.forEach(url -> registry.requestMatchers(url).permitAll());
            registry.requestMatchers(loginUrl).permitAll()
                    .anyRequest().permitAll();
                    // 所有请求都需要通过自定义的AuthorizationManager进行鉴权
                    //.anyRequest().access(customAuthorizationManager);
        }).exceptionHandling((exceptionHandling) -> exceptionHandling
                // 匿名处理
                .authenticationEntryPoint(new LoginAuthenticationHandler())
                // 无权限处理
                .accessDeniedHandler(new LoginAccessDefineHandler())
        );

        //loginPage:登录页面
        //loginProcessingUrl:登录接口
        //defaultSuccessUrl:登录成功之后访问的页面
        //successHandler:登录成功处理器
        //failureHandler:登录失败处理器
        //自定义登录接口
        httpSecurity.formLogin(formLogin -> {
            //这里更改SpringSecurity的认证接口地址，这样就默认处理这个接口的登录请求了
            formLogin.loginProcessingUrl("/login")
                    //自定义登录验证成功
                    .successHandler(new LoginSuccessHandler())
                    //自定义登录失败
                    .failureHandler(new LoginFailureHandler());
        });

        // 使用自己自定义的过滤器 去过滤接口请求
        //在登录之前使用验证码过滤器
        httpSecurity.addFilterBefore(new VerificationCodeFilter(), UsernamePasswordAuthenticationFilter.class);
        httpSecurity.addFilterBefore(checkTokenFilter, UsernamePasswordAuthenticationFilter.class);

        //配置自定义登录过滤器
        //将UsernamePasswordAuthenticationFilter替换掉
        httpSecurity.addFilterAt(loginFilter(), UsernamePasswordAuthenticationFilter.class);

        //rememberMeParameter：表单中记住我的name
        //key：是cookie加密的秘钥
        //httpSecurity.rememberMe(e -> e.rememberMeParameter("rememberMe")
        //         .key("myKey")
        //        .userDetailsService(customUserDetailsService));


        //跨域漏洞防御:关闭
        httpSecurity.csrf(AbstractHttpConfigurer::disable);

        //跨域拦截:关闭
        httpSecurity.cors(AbstractHttpConfigurer::disable);

        //session失效策略
        //httpSecurity.sessionManagement(e -> e.invalidSessionStrategy((request, response) -> {
        //    response.setContentType("text/html;charset=UTF-8");
        //    response.getWriter().write("会话过期");
        //}).maximumSessions(99).sessionRegistry(sessionRegistry()));

        //退出
        httpSecurity.logout(logout -> logout.invalidateHttpSession(true)
                .deleteCookies("rememberMe")
                .logoutSuccessUrl("logout")
                .logoutSuccessHandler((request, response, authentication) -> {
                    response.setContentType("text/html;charset=UTF-8");
                    response.getWriter().write("退出成功");
                }));

        return httpSecurity.build();
    }

    @Bean
    public LoginFilter loginFilter() throws Exception {
        LoginFilter loginFilter = new LoginFilter();
        loginFilter.setAuthenticationSuccessHandler(new LoginSuccessHandler());
        loginFilter.setAuthenticationFailureHandler(new LoginFailureHandler());
        loginFilter.setAuthenticationManager(authenticationConfiguration.getAuthenticationManager());
        return loginFilter;
    }

    @Bean
    public AuthenticationManager authenticationManager() throws Exception {
        return authenticationConfiguration.getAuthenticationManager();
    }

    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance();
    }

    /**
     * 获取所有当前登录的用户，以及设置用户过期
     *
     */
    @Bean
    public SessionRegistry sessionRegistry() {
        return new SessionRegistryImpl();
    }

    /**
     * 静态资源放行
     */
    @Bean
    public WebSecurityCustomizer webSecurityCustomizer() {
        return (webSecurity) -> webSecurity.ignoring().requestMatchers(
                "/swagger-ui/**",
                "/swagger-resources/**",
                "/v2/api-docs",
                "/webjars/**",
                "/doc.html"
        );
    }
}
